// MyDate.cpp
#include "MyDate.h"
#include <ctime>

using std::ostream;

// UTILITY FUNCTIONS
      // you might want to create some utility functions

// returns the number of days it's been since January 1st
int MyDate::DaysPassed(int month)
{
	switch (month)
	{
	case 1:
		return 0;
	case 2:
		return 31;
	case 3:
		return 59;
	case 4:
		return 90;
	case 5:
		return 120;
	case 6:
		return 151;
	case 7:
		return 181;
	case 8:
		return 212;
	case 9:
		return 243;
	case 10:
		return 273;
	case 11:
		return 304;
	case 12:
		return 334;
	}
}

// Returns what month it is based on the days passed into it
int MyDate::WhatMonth(int day)
{
	if (day < 31)
		return 1;

	if (day < 59)
		return 2;

	if (day < 90)
		return 3;

	if (day < 120)
		return 4;

	if (day < 151)
		return 5;

	if (day < 181)
		return 6;

	if (day < 212)
		return 7;

	if (day < 243)
		return 8;

	if (day < 273)
		return 9;

	if (day < 304)
		return 10;

	if (day < 334)
		return 11;

	if (day < 365)
		return 12;
}

// Returns what the maximum number of days a month can have
int MyDate::MaxDays(int month)
{
	switch(month)
	{
	case 1:
		return 31;
	case 2:
		if (this->IsLeapYear() == true)
			return 29;
		else 
			return 28;
	case 3:
		return 31;
	case 4:
		return 30;
	case 5:
		return 31;
	case 6:
		return 30;
	case 7:
		return 31;
	case 8:
		return 31;
	case 9:
		return 30;
	case 10:
		return 31;
	case 11:
		return 30;
	case 12:
		return 31;
	}
}

int MyDate::WhatDay(int days)
{
	if (IsLeapYear(_year - 1))
		days++;

	int tempdays = days % 7;
	return tempdays;
}


// CONSTRUCTORS
MyDate::MyDate() // default: sets date to jan 1, year 1
{
	_days = 0;
	_month = 1;
	_year = 1;
	_dayOfMonth = 1;
	_dayOfWeek = this->WhatDay(_days);
}

MyDate::MyDate(int day, int month, int year) // sets date to the day, month and year of the user's input
{
	_days = this->DaysPassed(month);
	_days += day - 1;
	_days += ((year - 1) * 365.2425);
	_dayOfMonth = day;
	_month = month;
	_year = year;
	_dayOfWeek = this->WhatDay(_days);
}

MyDate::MyDate(int days) // sets date to 'days' days from jan 1 year 1
{
	
	_days = days;
	_year = 365.2425 / _days;
	days = days - ((_year - 1 )* 365.2425);
	_month = this->GetMonth();
	_dayOfMonth = this->GetDay();
	_dayOfWeek = this->WhatDay(_days);
}

MyDate::MyDate(MyDate & aMyDate) // copy constructor 
{
	_days = aMyDate._days;
	_month = aMyDate._month;
	_year = aMyDate._year;
	_dayOfMonth = aMyDate._dayOfMonth;
	_dayOfWeek = aMyDate._dayOfWeek;
}

// METHODS
// returns True if the instance is a leap year
bool MyDate::IsLeapYear(MyDate & aMyDate)
{
	if ((aMyDate._year % 4 == 0) && (aMyDate._year % 100 != 0) || (aMyDate._year % 400 == 0))
		return true;
	else 
		return false;
}

bool MyDate::IsLeapYear(int year)
{
	if ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0))
		return true;
	else 
		return false;
}

bool MyDate::IsLeapYear()
{
	if ((_year % 4 == 0) && (_year % 100 != 0) || (_year % 400 == 0))
		return true;
	else 
		return false;
}

// Accessors and mutators for:
      // day, month, and year
      // returns int day of month

int MyDate::GetDay() 
{
	int TempDays = _days;
	int TempDayOfMonth;
	TempDays = TempDays - ((_year - 1 )* 365.2425) - (this->DaysPassed(_month));
	if (TempDays == 0)
		TempDayOfMonth = TempDays + 2;
	else
		TempDayOfMonth = TempDays + 1;
	return TempDayOfMonth;
}

// sets day of month (1-31), throws exception if attempting to set over the max days of a month
void MyDate::SetDay(int newDay)
{
	int TempMax = this->MaxDays(_month);
	if (newDay <= TempMax)
	{
		_dayOfWeek = this->WhatDay(newDay);
		_dayOfMonth = newDay;
	}
	else
		throw;
}

// returns int month of year ( jan = 1 dec = 12)
int MyDate::GetMonth()
{
	int tempDays = _days;
	tempDays = tempDays - ((_year - 1 )* 365.2425);
	int TempMonth = this->WhatMonth(tempDays);
	return TempMonth;
}

// sets month of year ( jan = 1 dec = 12), throws exception if new month doesn't have the day
void MyDate::SetMonth(int newMonth)
{
	if ((newMonth >= 1) && (newMonth <= 12))
	{
		_month = newMonth;
		this->SetDay(_dayOfMonth);
		_days = this->DaysPassed( _month);
		_days += _dayOfMonth - 1;
		_days += ((_year - 1) * 365.2425);
		_dayOfWeek = this->WhatDay(_days);
	}
	else
		throw;

}

// returns int year
int MyDate::GetYear()
{
	return _year;
}

// sets year
void MyDate::SetYear(int newYear)
{
	_year = newYear;
	_days = this->DaysPassed( _month);
	_days += _dayOfMonth - 1;
	_days += ((_year - 1) * 365.2425);
	if((this->IsLeapYear(newYear) == true) && (_month > 2))
		_days += 1;
	_dayOfWeek = this->WhatDay(_days);
}

// writes a cstring representation
	  //   of the current day of the week to dayBuffer
void MyDate::GetDayName(char * dayBuffer)
{
	int tempDayOfWeek = this->WhatDay(_days);
	switch(tempDayOfWeek)
	{
	case 0:
		dayBuffer = "Monday";
	case 1:
		dayBuffer = "Tuesday";
	case 2:
		dayBuffer = "Wednesday";
	case 3:
		dayBuffer = "Thursday";
	case 4:
		dayBuffer = "Friday";
	case 5:
		dayBuffer = "Saturday";
	case 6:
		dayBuffer = "Sunday";
	}
}

// writes a cstring representation
	  //   of the current month of the year to dayBuffer
void MyDate::GetMonthName(char * monthBuffer)
{
	int tempMonth = this->WhatMonth(_days);
	switch(tempMonth)
	{
	case 1:
		monthBuffer = "January";
	case 2:
		monthBuffer = "Febuary";
	case 3:
		monthBuffer = "March";
	case 4:
		monthBuffer = "April";
	case 5:
		monthBuffer = "May";
	case 6:
		monthBuffer = "June";
	case 7:
		monthBuffer = "July";
	case 8:
		monthBuffer = "August";
	case 9:
		monthBuffer = "September";
	case 10:
		monthBuffer = "October";
	case 11:
		monthBuffer = "November";
	case 12:
		monthBuffer = "December";
	}
}

// returns number of days since jan 1 year 1 for current instance date
int MyDate::GetDateSerial() const
{
	return _days;
}

// SetDate (various useful overloads)
            // Days
void MyDate::SetDate(int days)
{
	_days = days;
	_year =  ((_days + 1) / 365.2425) +1;
	days = days - ((_year - 1 )* 365.2425);
	_month = this->GetMonth();
	_dayOfMonth = this->GetDay();
	_dayOfWeek = this->WhatDay(_days);
}

// Day, Month Year
void MyDate::SetDate(int newDay, int newMonth, int newYear)
{
	_days = this->DaysPassed(newMonth);
	_days += newDay - 1;
	_days += ((newYear - 1) * 365.2425);
	_dayOfMonth = newDay;
	_month = newMonth;
	_year = newYear;
	_dayOfWeek = this->WhatDay(_days);
}

// MyDate
void MyDate::SetDate(const MyDate & aMyDate)
{
	_days = aMyDate._days;
	_month = aMyDate._month;
	_year = aMyDate._year;
	_dayOfMonth = aMyDate._dayOfMonth;
	_dayOfWeek = aMyDate._dayOfWeek;
}

// Now
      // a static function that returns a MyDate object
	  //   initialized to the current date according to the system clock
MyDate MyDate::Now()
{
	time_t rawtime;
    
    struct tm * timeinfo;

    time ( &rawtime );
    timeinfo = localtime ( &rawtime );

	int tempDay = timeinfo->tm_mday;
	int tempMonth = (timeinfo->tm_mon) + 1;
	int tempYear = (timeinfo->tm_year) + 1900;
	
    MyDate testTime(tempDay,tempMonth,tempYear); 
    return testTime;
}

// Add (various useful functions)
      // For adding amounts of time to this MyDate
            // Years
void MyDate::AddYears(int moreYears)
{
	_days -= _year * 365.2425;
	_year += moreYears;
	_days += _year * 365.2425;
	this->SetDate(_days);
}

// Months
void MyDate::AddMonths(int moreMonths)
{
	_days -= (this->DaysPassed(_month) + _dayOfMonth);
	_month += moreMonths;
	_days += (this->DaysPassed(_month) + _dayOfMonth);
	this->SetMonth(_month);
}

// Day
void MyDate::AddDays(long moreDays)
{
	_days += moreDays;
	this->SetDate(_days);
}


void MyDate::AddDate(const MyDate & aMyDate)
{
	_days += aMyDate._days + 1;
	this->SetDate(_days);
}


// Compare
      // Takes a MyDate argument
      // Returns (zero) if the argument has the same time as this MyDate
      // Returns (a positive int)  if the argument is before this MyDate
      // Returns (a negative int)  if the argument is after this MyDate
int MyDate::Compare(const MyDate & aMyDate) const
{
	if (_days == aMyDate._days)
		return 0;
	else if (_days > aMyDate._days)
		return 1;
	else if (_days < aMyDate._days)
		return -1;
}

// Equals
      // Takes a MyDate argument
      // Returns (true) if the argument has the same time
	  //   as this MyDate, otherwise it returns (false).
bool MyDate::Equals(const MyDate & aMyDate) const
{
	if (_days == aMyDate._days)
		return true;
	else 
		return false;
}

// Subtract (various useful functions)
            // Years
void MyDate::SubtractYears(int lessYears)
{
	_days -= _year * 365.2425;
	_year -= lessYears;
	_days += _year * 365.2425;
	this->SetDate(_days);
}
		 // Months
void MyDate::SubtractMonths(int lessMonths)
{
	_days -= (this->DaysPassed(_month) + _dayOfMonth);
	_month -= lessMonths;
	_days += (this->DaysPassed(_month) + _dayOfMonth);
	this->SetMonth(_month);
}

		// Days
void MyDate::SubtractDays(long lessDays)
{
	_days -= lessDays;
	this->SetDate(_days);
}

// MyDate
void MyDate::SubtractDate(const MyDate & aMyDate)
{
	_days -= aMyDate._days;
	this->SetDate(_days);
}	

// OVERLOADED OPERATORS
      // = (assignment - takes a MyDate)
MyDate MyDate::operator= (const MyDate & aMyDate)
{
	this->SetDate(aMyDate);
	return *this;
}

// +, += (Addition - takes a MyDate)
MyDate MyDate::operator+ (const MyDate & aMyDate) const
{
	MyDate CopyDate(_days);
	CopyDate.AddDate(aMyDate);	
	return CopyDate;
	
}

MyDate MyDate::operator+= (const MyDate & aMyDate)
{
	MyDate CopyDate(_days);
	CopyDate.AddDate(aMyDate);
	this->SetDate(CopyDate);
	return CopyDate;
}

// >, <, >=, <=, ==, != (boolean relational test operators)
bool MyDate::operator> (const MyDate & aMyDate)  const
{
	if (_days > aMyDate._days)
		return true;
	else 
		return false;
}
bool MyDate::operator< (const MyDate & aMyDate)  const
{
	if (_days < aMyDate._days)
		return true;
	else
		return false;
}
bool MyDate::operator>= (const MyDate & aMyDate)  const
{
	if (_days >= aMyDate._days)
		return true;
	else
		return false;
}
bool MyDate::operator<= (const MyDate & aMyDate)  const
{
	if (_days <= aMyDate._days)
		return true;
	else 
		return false;
}
bool MyDate::operator== (const MyDate & aMyDate)  const
{
	if (this->Equals(aMyDate) == true)
		return true;
	else 
		return false;
}

bool MyDate::operator!= (const MyDate & aMyDate)  const
{
	if (this->Equals(aMyDate) == false)
		return true;
	else
		return false;
}

// <<, >> stream insertion and extraction
ostream & operator<< (ostream & os, MyDate & aMyDate)
{
	std::cout << aMyDate.GetMonth() << "/" << aMyDate.GetDay() << "/" << aMyDate.GetYear();
	return os;
}

// date must be one of these formats
      //    ???
istream & operator>> (istream & is, MyDate & aMyDate)
{
	int x;
	int ans;
	std::cout << "Press 1 then enter to input the number of days, Press 2 then enter to input the Month, Day, Year \n";
	std::cin >> x;
	if (x == 1)
		std::cin >> aMyDate._days;
	else if (x == 2)
	{
		std::cout << "Enter Month: \n";
		std::cin >> ans;
		aMyDate.SetMonth(ans);
		std::cout << "Enter day: \n";
		std::cin >> ans;
		aMyDate.SetDay(ans);
		std::cout << "Enter Year: \n";
		aMyDate.SetDay(ans);
	}
	else 
		std::cout << "That is not a valid selection\n";

	return is;
}